//    Copyright (C) Kherty.  All rights reserved.

using System;

namespace OpenLS.Spreadsheet
{
    sealed class RangeNode: RangeNodeBase
    {
        public RangeNode(Range range)
        {
            _range = range;
            foreach (var cell in range.GetNonEmptyCells())
            {
                cell.AddDependent(this);
            }
            range.Worksheet.AddWeakCellAdded(onCellAdded);
            range.Worksheet.AddWeakCellMoved(onCellMoved);
            range.Worksheet.AddWeakRangeChanged(OnRangeChanged);
        }

        private void OnRangeChanged(object sender, RangeChangeEventArgs e)
        {
            if (e.Type == RangeChangeType.Deletion)
            {
                RangeAddress toConsider;

                switch (e.Hint)
                {
                    case CellMoveOrientationHint.Vertical:
                        toConsider = new RangeAddress(Range.Address.BottomLeft - new CellRelativeAddress(e.Address.RowSpan, 0), Range.Address.BottomRight);
                        break;
                    case CellMoveOrientationHint.Horizontal:
                        toConsider = new RangeAddress(Range.Address.TopRight - new CellRelativeAddress(0, e.Address.ColumnSpan), Range.Address.BottomRight);
                        break;
                    default:
                        throw new NotSupportedException();
                }
                foreach (IDependencyNode cell in CellsRange.Create(Range.Worksheet, toConsider))
                {
                    this.RemoveDependent(cell);
                }
            }
        }


        public override string ToString()
        {
            return string.Format("{0}[{1}]", base.ToString(), _range.Label);
        }
#if DEBUG
        protected override bool ValidToAdd(IDependencyNode node)
        {
            return Enumerable.All(Range.GetNonEmptyCells(), c => c != node);
        }
#endif

        static bool Contains (Range range, CurrentDeletion currentDelection, Cell cell)
        {
            if (currentDelection == null)
                return range.Contains(cell);
            if (range is MultiCellsRange)
            {
                var a = ((MultiCellsRange)range).GetNewRangeAddress(RangeChangeType.Deletion, currentDelection.Hint,
                                                                     currentDelection.RangeAddress);
                if (a != null)
                    return a.Value.Contains(cell.Address);
            }
            if (range is CompositeRange)
            {
                CompositeRange cr = (CompositeRange) range;
                return Enumerable.Any(cr.Ranges, r => Contains(r, currentDelection, cell));
            }
            return range.Contains(cell);
        }
        static bool Contains(Range range, CurrentDeletion currentDeletion, CellAddress cell)
        {
            if (currentDeletion == null)
                return range.Contains(cell);
            if (range is MultiCellsRange)
            {
                var a = ((MultiCellsRange)range).GetNewRangeAddress(RangeChangeType.Deletion, currentDeletion.Hint,
                                                                     currentDeletion.RangeAddress);
                if (a != null)
                    return a.Value.Contains(cell);
            }
            if (range is CompositeRange)
            {
                CompositeRange cr = (CompositeRange)range;
                return Enumerable.Any(cr.Ranges, r => Contains(r, currentDeletion, cell));
            }
            return range.Contains(cell);
        }
        private void onCellMoved(object sender, CellMovedArgs args)
        {
            bool wasContaining = this.Range.Contains(args.Origin);//\\ Contains(Range, args, args.Origin);
            bool willContain = Contains (Range, Range.Worksheet.CurrentDeletion, args.Cell);
            if (willContain == wasContaining)
                return;
            IDependencyNode c = args.Cell;
            if (wasContaining)
                c.RemoveDependent(this);
            else
            {
                if (willContain)
                    c.AddDependent(this);
            }
        }

        private readonly Range _range;

        public Range Range
        {
            get { return _range; }
        }

        private void onCellAdded (object sender, CellEventArgs args)
        {
            Cell cell = args.Cell;
            if ( Contains(Range, this.Range.Worksheet.CurrentDeletion, cell))
                cell.AddDependent(this);
        }
    }
}
